import os
import random
import re
import time

from django.http import FileResponse

# Create your views here.
from rest_framework.throttling import AnonRateThrottle

from common.aliyun_sm import AliyunSMS
from users.models import User, Addr, VerifCode, City
from users.permissions import UserPermissions, AddrPermissions
from users.serializers import UserSerializer, AddrSerializer, CitySerializer

from rest_framework import status, mixins
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import GenericViewSet
from rest_framework.permissions import IsAuthenticated

from rest_framework_simplejwt.exceptions import TokenError, InvalidToken
from rest_framework_simplejwt.views import TokenObtainPairView

from web_shop.settings import MEDIA_ROOT


class RegisterView(APIView):

    def post(self, request):
        """ 注册 """
        # 1,接收用户参数
        username = request.data.get('username')
        password = request.data.get('password')
        email = request.data.get('email')
        password_confirmation = request.data.get('password_confirmation')
        # 2,参数校验
        # 校验参数是否为空
        if not all([username, password, email, password_confirmation]):
            return Response({
                'message': "注册失败",
                'error': '所有参数不能为空',
            }, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        # 校验用户是否已注册
        if User.objects.filter(username=username).exists():
            return Response({'message': "注册失败", 'error': '用户名已注册'}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        # 校验密码是否一致
        if password != password_confirmation:
            return Response({'error': '密码和确认密码不一致'}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        # 校验密码长度
        if not 6 <= len(password) <= 18:
            return Response({'error': '密码长度需要到6-18位'}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        # 校验邮箱是否合规
        if User.objects.filter(email=email).exists():
            return Response({'error': '该邮箱也被绑定在其他用户'}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        if not re.match(r"[^@]+@[^@]+\.[^@]+", email):
            return Response({'error': '邮箱格式有误'}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)

        # 创建用户
        obj = User.objects.create_user(username=username, email=email, password=password)
        res = {
            'id': obj.id,
            "username": username,
            'email': email
        }
        return Response(res, status=status.HTTP_201_CREATED)


class LoginView(TokenObtainPairView):
    """ 用户登录 """

    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        try:
            serializer.is_valid(raise_exception=True)
        except TokenError as e:
            raise InvalidToken(e.args[0])

        # 自定义登录成功后返回结果
        result = serializer.validated_data
        result['token'] = result.pop('access')

        result['id'] = serializer.user.id
        result['mobile'] = serializer.user.mobile
        result['email'] = serializer.user.email
        result['username'] = serializer.user.username
        # result['password'] = serializer.user.password
        return Response(result, status=status.HTTP_200_OK)


class UserView(GenericViewSet, mixins.RetrieveModelMixin, mixins.UpdateModelMixin):
    # class UserView(ModelViewSet):
    """ 用户相关操作  """
    queryset = User.objects.all()
    serializer_class = UserSerializer

    # 设置认证用户才能访问
    permission_classes = [IsAuthenticated, UserPermissions]

    # 自定义的查自己的信息 和 自带的 retrieve 方法一样
    def get(self, request, pk, format=None):
        # user = self.get_object(id)
        user = self.get_object()
        serializer = self.get_serializer(instance=user, many=False)
        print('serializer', serializer.data)
        return Response({'code': 200, 'data': serializer.data, 'status': 200})

    def upload_avatar(self, request, *args, **kwargs):
        """ 上传用户头像 """
        avatar = request.data.get('avatar')
        # 校验文件是否为空
        if not avatar:
            return Response({'error': '上传失败，未选择文件'}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        # 校验上传图片不能大于300kb
        if avatar.size > 1024 * 300:
            return Response({'error': '上传失败，文件大小不能超过300kb'}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        # 保存文件
        user = self.get_object()  # 获取用户信息
        # partial=True 设置序列化器 只判断 里面的数据 不是 整个
        serializer = self.get_serializer(user, data={'avatar': avatar}, partial=True)  # 获取序列化对象
        serializer.is_valid(raise_exception=True)  # 校验
        serializer.save()  # 保存
        # 返回序列化数据中的avatar 这个字段
        return Response({'url': serializer.data['avatar']})

    @staticmethod
    def verif_code(code, code_id, mobile):
        """
        :param code: 验证码
        :param code_id: 验证码id
        :param mobile: 手机号
        :return: 验证通过返回None，失败返回Response
        """
        # 2, 校验参数
        if not code:
            return {'error': '验证码未接收到'}
        if not code_id:
            return {'error': '验证码ID未接收到'}
        if not mobile:
            return {'error': '手机号未接收'}

        # 3，校验验证码
        if VerifCode.objects.filter(id=code_id, code=code, mobile=mobile).exists():
            # 验证码存在
            # 校验验证码是否过期【过期时间为3分钟
            # c_obj = VerifCode.objects.filter(id=code_id, code=code, mobile=mobile)[0]
            c_obj = VerifCode.objects.get(id=code_id, code=code, mobile=mobile)
            print('c_obj', c_obj.id)
            ct = c_obj.create_time.timestamp()  # 获取创建验证码的时间的时间戳
            # 获取当前的时间戳
            et = time.time()
            print(ct, et)
            # 删除这个验证码【避免出现用户再有效期类，重复使用一个验证码
            c_obj.delete()
            if ct + 180 < et:
                return {'error': '验证码过期，请重新获取  '}
        else:
            return {'error': '验证码验证失败，请重新获取'}

    def bind_mobile(self, request, *args, **kwargs):
        """ 绑定手机方法 """
        # 1, 获取参数
        code = request.data.get('code')  # 获取验证码
        code_id = request.data.get('codeID')  # 获取验证码id
        mobile = request.data.get('mobile')  # 获取手机号
        print(code, code_id, mobile)
        # 2, 校验参数
        if not code:
            return Response({'error': '验证码未接收到'}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        if not code_id:
            return Response({'error': '验证码ID未接收到'}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        if not mobile:
            return Response({'error': '手机号未接收'}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)

        # 3，校验验证码
        if VerifCode.objects.filter(id=code_id, code=code, mobile=mobile).exists():
            # 验证码存在
            # 校验验证码是否过期【过期时间为3分钟
            # c_obj = VerifCode.objects.filter(id=code_id, code=code, mobile=mobile)[0]
            c_obj = VerifCode.objects.get(id=code_id, code=code, mobile=mobile)
            print('c_obj', c_obj.id)
            ct = c_obj.create_time.timestamp()  # 获取创建验证码的时间的时间戳
            # 获取当前的时间戳
            et = time.time()
            print(ct, et)
            # 删除这个验证码【避免出现用户再有效期类，重复使用一个验证码
            c_obj.delete()
            if ct + 180 < et:
                return Response({'error': '验证码过期，请重新获取  '}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        else:
            return Response({'error': '验证码验证失败，请重新获取'}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        # self.verif_code(code, code_id, mobile)

        # 4，绑定手机号
        if User.objects.filter(mobile=mobile).exists():  # 校验手机号是否已被绑定
            return Response({'error': '该手机号已被其他用户绑定，请换个手机号重绑'}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        # 绑定【把手机号保存到用户的mobile字段上
        user = request.user
        user.mobile = mobile
        user.save()
        return Response({'message': '绑定成功'}, status=status.HTTP_200_OK)

    def unbind_mobile(self, request, *args, **kwargs):
        """ 解绑手机号 """
        # 1, 获取参数
        code = request.data.get('code')  # 获取验证码
        code_id = request.data.get('codeID')  # 获取验证码id
        mobile = request.data.get('mobile')  # 获取手机号
        print(code, code_id, mobile)
        # 校验参数，验证码
        result = self.verif_code(code, code_id, mobile)
        if result:
            return Response(result, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        # 解绑手机号【验证用户已绑定的手机号
        user = request.user
        if (user.mobile == mobile):  # 判断用户绑定的手机号和发送验证码的手机号是否一致
            user.mobile = ''  # 一样则设为空
            user.save()
            return Response({'message': '解绑成功'}, status=status.HTTP_200_OK)
        else:
            return Response({'error': '当前用户没有绑定该手机号'}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)

    def update_name(self, request, *args, **kwargs):
        """ 修改用户名 """
        # 获取参数
        last_name = request.data.get('last_name')
        # 校验参数
        if not last_name:
            return Response({'error': '参数last_name不能为空'}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        # 修改用户名
        user = self.get_object()  # 获取当前用户
        user.last_name = last_name
        user.save()
        return Response({'message': '修改成功'}, status=status.HTTP_200_OK)

    def update_email(self, request, *args, **kwargs):
        """ 修改邮箱 """
        # 获取参数
        email = request.data.get('email', '')
        print(email)
        # 校验参数
        if not email:
            return Response({'error': '参数last_name不能为空'}, status=status.HTTP_401_UNAUTHORIZED)
        # 使用正则判断邮箱号是否合规
        res = re.match(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}", email)
        if not res:
            return Response({'error': '无效的邮箱号'}, status=status.HTTP_401_UNAUTHORIZED)

        # 判断修改邮箱和之前是否一样
        user = self.get_object()
        if user.email == email:
            return Response({'message': 'OK,邮箱一致，无需修改'}, status=status.HTTP_200_OK)
        # 判断该邮箱是否绑定了其他用户
        # for item in User.objects.all():
        #     if item.email == email:
        #         return Response({'error': '该邮箱已绑定其他用户'}, status=status.HTTP_401_UNAUTHORIZED)

        if User.objects.filter(email=email).exists():
            return Response({'error': '该邮箱已绑定其他用户'}, status=status.HTTP_401_UNAUTHORIZED)

        # 没有就更换
        # 修改邮箱
        user.email = email
        user.save()
        return Response({'message': '修改成功'})

    def update_password(self, request, *args, **kwargs):
        """ 修改重置密码 """
        user = self.get_object()

        # 1，获取参数
        code = request.data.get('code')
        code_id = request.data.get('codeID')
        mobile = request.data.get('mobile')
        password = request.data.get('password')
        password_confirmation = request.data.get('password_confirmation')
        # 2，校验验证码
        result = self.verif_code(code, code_id, mobile)
        if result:
            return Response(result, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        # 校验用户绑定手机和发验证码的手机是否一致
        if user.mobile != mobile:
            return Response({'error': '此验证码和绑定手机号有误！'}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        # 3，校验密码
        if not password:
            return Response({'error': '密码不能为空！'}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        if not password_confirmation:
            return Response({'error': '确认密码不能为空！'}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        if password != password_confirmation:
            return Response({'error': '两次输入密码不一致！'}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        # 4，修改密码
        # set_password 自动加密保存
        user.set_password(password)
        print(user.set_password(password))
        user.save()
        return Response({'message': '密码修改成功'})


class FileView(APIView):
    # 获取文件视图
    def get(self, request, name):
        path = MEDIA_ROOT / name
        print(path)
        if os.path.isfile(path):
            return FileResponse(open(path, 'rb'))
        return Response({'error': '没有找到该文件'}, status=status.HTTP_404_NOT_FOUND)


class AddrView(GenericViewSet,
               mixins.ListModelMixin,
               mixins.CreateModelMixin,
               mixins.DestroyModelMixin,
               mixins.UpdateModelMixin):
    """ 地址相关的视图集 """
    queryset = Addr.objects.all()
    serializer_class = AddrSerializer

    # 设置认证用户才能访问
    permission_classes = [IsAuthenticated, AddrPermissions]

    # 自定过滤字段
    filterset_fields = ('user',)  # 判断是否相等

    def list(self, request, *args, **kwargs):
        """ 查看自己的地址 """
        queryset = self.filter_queryset(self.get_queryset())
        # 通过请求过来的用户进行过滤
        queryset = queryset.filter(user=request.user)

        # 分页
        # page = self.paginate_queryset(queryset)
        # if page is not None:
        #     serializer = self.get_serializer(page, many=True)
        #     return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

    def set_default_addr(self, request, *args, **kwargs):
        """ 设置默认收货地址 """
        # 当有默认地址的时候可以用
        # addr = Addr.objects.get(is_default=True)
        # addr.is_default = False
        # addr.save()

        # 1，获取要设置默认地址的对象
        obj = self.get_object()
        obj.is_default = True
        obj.save()
        # 2，将这个地址对象设置为默认地址
        # 3，将以前的默认地址设置为非默认地址

        # 查询出登录用户的所有地址
        queryset = self.get_queryset().filter(user=request.user)
        # 循环遍历出每一个地址
        for item in queryset:
            # 判断出不等于上面设置为默认地址以为的地址对象
            if item != obj:
                # 设置为非默认地址
                item.is_default = False
                item.save()

        return Response({'massage': '设置成功！'}, status=status.HTTP_200_OK)


class SendSmView(APIView):
    """ 发送短信验证码 """

    # 设置限流【每分钟只能访问一次
    throttle_classes = (AnonRateThrottle,)

    def post(self, request):
        # 获取手机号
        mobile = request.data.get('mobile', '')
        # 验证手机号格式是否正确
        res = re.match(r'^(13|14|15|17|18)\d{9}$', mobile)
        if not res:
            return Response({'error': '无效的手机号'}, status=status.HTTP_422_UNPROCESSABLE_ENTITY)

        # 随机生成一个6位数的验证码
        code = self.get_random_code()

        # 正确的手机号 就发送短信验证码
        result = AliyunSMS().send_msg(mobile=mobile, code=code)
        print(code, result)

        # 判断是否发送成功
        if result['code'] == 'OK':
            # 成功发送后将验证码汝入库
            obj = VerifCode.objects.create(mobile=mobile, code=code)
            obj.save()
            # 把验证码的id 返回给用户，方便后面校验
            result['codeID'] = obj.id
            return Response(result, status=status.HTTP_200_OK)
        else:
            return Response(result, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    def get_random_code(self):
        """ 随机生成一个6位数的验证码 """
        # code2 = ''.join(random.choices('0123456789', k=6))
        code = ''
        for i in range(6):
            num = random.randint(0, 9)
            code += str(num)
        return code


class CityView(GenericViewSet, mixins.ListModelMixin):
    """ 省市区县数据查询接口 """
    queryset = City.objects.all()
    serializer_class = CitySerializer

    # 过滤查询
    filterset_fields = ('leveltype', 'citycode',)
